﻿namespace PowerSong.Indexer
    {
    using System;
    using System.Collections.Generic;

    public class Searcher
        {

        private Indexer index = null;

        public Searcher(Indexer index)
            {
            this.index = index;
            }

        private Dictionary<int, Score> SearchInternal(params string[] terms)
            {

            // Build a list of all the documents, each initially with a score of 0
            var result = new Dictionary<int, Score>();
            foreach (int documentNumber in index.AllDocumentsNumbers)
                {
                result.Add(documentNumber, new Score());
                }

            // Process all terms in the query
            var completedTerms = new List<string>();
            foreach (string term in terms)
                {
                var upperTerm = term.ToUpper();

                // Process only unique terms
                if (!completedTerms.Contains(upperTerm))
                    {
                    int queryWordFrequency = GetQueryTermFrequency(upperTerm, terms);

                    // Calculate a score for the term and add it to the current score for the document
                    List<int> documents = index.DocumentsContainingWord(upperTerm);
                    foreach (int documentIndex in documents)
                        {
                        double termFrequency = index.TermFrequency(documentIndex, upperTerm);
                        double logNumber = Math.Log(index.DocumentCount / documents.Count);
                        double logRuleScore = termFrequency * queryWordFrequency * logNumber;
                        Score documentScore = result[documentIndex];
                        documentScore.LogRuleScore += logRuleScore;
                        documentScore.Match = true;
                        }

                    completedTerms.Add(upperTerm);
                    }

                }

            // Return the result
            return result;

            }

        /// <summary>
        /// Sorts the search results.
        /// </summary>
        /// <param name="results">The results (from Search() above) to sort.</param>
        /// <returns>The list of document indices, ordered by score.</returns>
        private List<int> GetSortedDocumentOrder(Dictionary<int, Score> results)
            {

            // Sort the scores
            List<double> sortedScores = new List<double>();
            foreach (Score score in results.Values)
                {
                if (!sortedScores.Contains(score.LogRuleScore)) sortedScores.Add(score.LogRuleScore);
                }
            sortedScores.Sort();

            // Create an ordered list of document indices
            List<int> sortedDocuments = new List<int>();
            foreach (double score in sortedScores)
                {
                foreach (KeyValuePair<int, Score> searchResult in results)
                    {
                    if (searchResult.Value.LogRuleScore == score) sortedDocuments.Add(searchResult.Key);
                    }
                }

            // Reverse the list and return it
            sortedDocuments.Reverse();
            return sortedDocuments;

            }

        /// <summary>
        /// Determine the number of times the term appears in the query.
        /// </summary>
        private int GetQueryTermFrequency(string term, string[] allTerms)
            {

            term = term.ToUpper();

            int frequency = 0;
            foreach (string currentTerm in allTerms)
                {
                if (currentTerm.ToUpper() == term) frequency += 1;
                }
            return frequency;

            }

        public List<string> Search(string query)
            {

            // Perform the search
            var splitters = new string[] { " ", "!", ".", ":", "\"", "-", Environment.NewLine };
            var result = new List<string>();
            Dictionary<int, Score> searchResults = SearchInternal(query.Split(splitters, StringSplitOptions.RemoveEmptyEntries));

            // Return the results in sorted order
            foreach (int documentIndex in GetSortedDocumentOrder(searchResults))
                {
                Score score = searchResults[documentIndex];
                if (score.Match) result.Add(index.GetDocumentName(documentIndex));
                }
            return result;

            }

        }

    }
